package com.turman.plyopengl

import android.opengl.GLES20.GL_COMPILE_STATUS
import android.opengl.GLES20.GL_FRAGMENT_SHADER
import android.opengl.GLES20.GL_LINK_STATUS
import android.opengl.GLES20.GL_VALIDATE_STATUS
import android.opengl.GLES20.GL_VERTEX_SHADER
import android.opengl.GLES20.glAttachShader
import android.opengl.GLES20.glCompileShader
import android.opengl.GLES20.glCreateProgram
import android.opengl.GLES20.glCreateShader
import android.opengl.GLES20.glDeleteProgram
import android.opengl.GLES20.glDeleteShader
import android.opengl.GLES20.glGetError
import android.opengl.GLES20.glGetProgramInfoLog
import android.opengl.GLES20.glGetProgramiv
import android.opengl.GLES20.glGetShaderInfoLog
import android.opengl.GLES20.glGetShaderiv
import android.opengl.GLES20.glLinkProgram
import android.opengl.GLES20.glShaderSource
import android.opengl.GLES20.glValidateProgram

object ShaderHelper {
    private const val tag = "ShaderHelper"

    fun compileVertexShader(shaderCode: String) = compileShader(GL_VERTEX_SHADER, shaderCode)

    fun compileFragmentShader(shaderCode: String) = compileShader(GL_FRAGMENT_SHADER, shaderCode)

    /**
     * 编译shader 返回shader句柄
     */
    fun compileShader(type: Int, code: String): Int {
        val shaderObjectId = glCreateShader(type)
        if (type == 0) {
            LogUtil.w(tag = tag, message = "Could not create new shader:" + glGetError())
            return 0
        }
        glShaderSource(shaderObjectId, code)
        glCompileShader(shaderObjectId)

        //获取编译状态
        val compileStatus: IntArray = IntArray(1)
        glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0)
        //打印编译日志
        LogUtil.v(
            tag = tag, message = "Results of compiling source:" +
                    "\n$code\n:${glGetShaderInfoLog(shaderObjectId)}"
        )
        if (compileStatus[0] == 0) {
            //编译失败则删除对象并退出
            glDeleteShader(shaderObjectId)
            LogUtil.w(tag, "Compilation of shader failed.")
            return 0
        }
        return shaderObjectId
    }

    /**
     * 链接shader，返回链接程序句柄
     */
    fun linkProgram(vertexShaderId: Int, fragmentShaderId: Int): Int {
        val programObjectId = glCreateProgram()
        if (programObjectId == 0) {
            LogUtil.w(tag, "Could not create new program:" + glGetError())
            return 0
        }
        glAttachShader(programObjectId, vertexShaderId)
        glAttachShader(programObjectId, fragmentShaderId)
        glLinkProgram(programObjectId)
        val linkStatus = IntArray(1)
        glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0)
        LogUtil.v(tag, "Result of linking program:\n${glGetProgramInfoLog(programObjectId)}")
        if (linkStatus[0] == 0) {
            glDeleteProgram(programObjectId)
            LogUtil.w(tag, "Linking of program failed.")
            return 0
        }
        return programObjectId
    }

    /**
     * 验证程序
     */
    fun validateProgram(programObjectId: Int): Boolean {
        glValidateProgram(programObjectId)
        val validateStatus = IntArray(1)
        glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0)
        LogUtil.v(
            tag,
            "Results of validating program: " + validateStatus[0] + "\nLog:" + glGetProgramInfoLog(
                programObjectId
            )
        )
        return validateStatus[0] != 0

    }

    fun buildProgram(vertexShaderSource: String, fragmentShaderSource: String): Int {
        val vertexShaderId = compileVertexShader(vertexShaderSource)
        val fragmentShaderId = compileFragmentShader(fragmentShaderSource)
        val program = linkProgram(vertexShaderId,fragmentShaderId)
        validateProgram(program)
        return program
    }

}